home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr46
/
vfwdk.zip
/
VFWSDK.ZIP
/
SAMPLES
/
WRITEAVI
/
AVIEASY.C
next >
Wrap
C/C++ Source or Header
|
1993-01-31
|
21KB
|
728 lines
/****************************************************************************
*
* AVIEASY.C
*
* low-level routines for writing Standard AVI files
*
* AVIPhys...()
*
* Copyright (c) 1992-1993 Microsoft Corporation. All Rights Reserved.
*
* You have a royalty-free right to use, modify, reproduce and
* distribute the Sample Files (and/or any modified version) in
* any way you find useful, provided that you agree that
* Microsoft has no warranty obligations or liability for any
* Sample Application Files which are modified.
*
***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <compman.h>
#include <memory.h>
#include "avifmt.h"
#include "avieasy.h"
// C6 needs a little help....
#undef GlobalFreePtr
#define GlobalFreePtr(lp) (BOOL)GlobalFree(GlobalPtrHandle(lp))
extern LONG FAR PASCAL muldiv32(LONG,LONG,LONG);
#define MAXSTREAMS 16
typedef struct {
HMMIO hmmio;
DWORD dwStart;
int iMaxStream;
LPVOID lpFormat[MAXSTREAMS];
DWORD cbFormat[MAXSTREAMS];
AVIStreamHeader strhdr[MAXSTREAMS];
AVIINDEXENTRY huge * hpIndex;
DWORD dwIndex;
DWORD dwIndexAlloc;
DWORD dwIndexRec;
MMCKINFO ckRECORD;
MainAVIHeader avihdr;
} PHYSINFO, FAR *PPHYS;
/* Some simple functions for dealing with AVI indices. */
static BOOL NEAR PASCAL InitIndex(PPHYS pphys);
static BOOL NEAR PASCAL AddChunkToIndex(PPHYS pphys, MMCKINFO FAR * lpck, DWORD dwFlags);
static BOOL NEAR PASCAL WriteIndex(PPHYS pphys);
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileOpen | Open an AVI file for writing.
*
* @parm HAVI FAR * | lphfile | Holds the returned file handle if the
* function succeeds.
*
* @parm LPSTR | lpFileName | The file name to use.
*
* @parm MainAVIHeader FAR * | lphdr | The main AVI header to write to the file.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileClose
****************************************************************************/
LONG FAR avifileOpen(HAVI FAR *lphfile, LPSTR lpFileName, MainAVIHeader FAR *lphdr)
{
PPHYS pwrite;
LONG lRet = AVIERR_OK;
pwrite = (PPHYS) GlobalAllocPtr(GHND, sizeof(PHYSINFO));
if (!pwrite)
return AVIERR_MEMORY;
pwrite->hmmio = mmioOpen(lpFileName, NULL, MMIO_WRITE | MMIO_CREATE);
pwrite->avihdr = *lphdr;
pwrite->avihdr.dwStreams = 0; // we'll use the # of streams they add
if (!pwrite->hmmio)
goto OpenError;
if (!InitIndex(pwrite))
goto FileError;
*lphfile = (HAVI) pwrite;
goto exit;
FileError:
lRet = AVIERR_FILEWRITE;
mmioClose(pwrite->hmmio, 0);
goto exit;
OpenError:
lRet = AVIERR_FILEOPEN;
exit:
return lRet;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileAddStream | Add a new stream to an AVI file.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @parm int FAR * | lpstream | A pointer to an integer that will receive
* the number of the new stream. Can be NULL.
*
* @parm AVIStreamHeader FAR * | lphdr | The stream's header information.
*
* @parm LPVOID | lpFormat | A pointer to the stream's format, such as a
* <t BITMAPINFO> or <t WAVEFORMAT> structure.
*
* @parm LONG | cbFormat | The size of the format pointed to be <p lpFormat>.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileOpen avifileWriteToHeader
****************************************************************************/
LONG FAR avifileAddStream(HAVI hfile, int FAR *lpstream,
AVIStreamHeader FAR *lphdr,
LPVOID lpFormat,
LONG cbFormat)
{
PPHYS pwrite = (PPHYS) hfile;
int stream = (int) pwrite->avihdr.dwStreams++;
if (lpstream)
*lpstream = stream;
pwrite->cbFormat[stream] = cbFormat;
pwrite->lpFormat[stream] = GlobalAllocPtr(GMEM_MOVEABLE, cbFormat);
// !!!
hmemcpy(pwrite->lpFormat[stream], lpFormat, cbFormat);
pwrite->strhdr[stream] = *lphdr;
pwrite->strhdr[stream].dwLength = 0;
// Fix up values in header...
if (lphdr->fccType == streamtypeAUDIO) {
LPWAVEFORMAT lpwf = (LPWAVEFORMAT) lpFormat;
pwrite->strhdr[stream].dwSampleSize = lpwf->nBlockAlign;
pwrite->strhdr[stream].dwRate = lpwf->nSamplesPerSec;
pwrite->strhdr[stream].dwScale = lpwf->nBlockAlign;
} else {
pwrite->strhdr[stream].dwSampleSize = 0;
// Default to 15/sec....
if (pwrite->strhdr[stream].dwRate == 0 ||
pwrite->strhdr[stream].dwScale == 0) {
pwrite->strhdr[stream].dwRate = 15;
pwrite->strhdr[stream].dwScale = 1;
}
}
if (lphdr->fccType == streamtypeVIDEO) {
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) lpFormat;
if (pwrite->avihdr.dwWidth < (DWORD) lpbi->biWidth)
pwrite->avihdr.dwWidth = lpbi->biWidth;
if (pwrite->avihdr.dwHeight < (DWORD) lpbi->biHeight)
pwrite->avihdr.dwHeight = lpbi->biHeight;
}
return AVIERR_OK;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileWriteToHeader | Add data to the header of an AVI file.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @parm int | stream | The stream to write to. Can be -1, which indicates
* that the data should be associated with the file as a whole, rather
* than a single stream.
*
* @parm DWORD | ckid | The RIFF ckid to use for the data.
*
* @parm LPVOID | lpData | A pointer to the data to be written
*
* @parm LONG | cbData | The size of the data pointed to be <p lpData>.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileAddStream
****************************************************************************/
LONG FAR avifileWriteToHeader(HAVI hfile,
int stream,
DWORD ckid,
LPVOID lpData,
LONG cbData)
{
// !!! Add this data onto a list kept for each stream, so we can write
// it later.
// stream -1 means main header?
return AVIERR_OK;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileWrite | Write data to an AVI file.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @parm int | stream | The stream to write to.
*
* @parm LPVOID | lpData | A pointer to the data to be written
*
* @parm LONG | cbData | The size of the data pointed to be <p lpData>.
*
* @parm WORD | cktype | The TWOCC to use for the data. Mostly obsolete,
* except for RGB and RLE DIB data.
*
* @parm DWORD | dwFlags | Flags associated with this data. In particular:
*
* @flag AVIIF_KEYFRAME | This data represents a key frame.
*
* @flag AVIIF_NOTIME | This data is control data, and does not take up
* a frame's worth of time.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileAddStream
****************************************************************************/
LONG FAR avifileWrite(HAVI hfile,
int stream,
LPVOID lpData,
LONG cbData,
WORD cktype,
DWORD dwFlags)
{
PPHYS pwrite = (PPHYS) hfile;
MMCKINFO ck;
DWORD dw;
int i;
if (stream >= (int) pwrite->avihdr.dwStreams)
return AVIERR_BADPARAM;
if (cktype < 256 * ' ')
cktype = aviTWOCC('x', 'x');
if (pwrite->dwStart == 0) {
// Figure out where to start writing the data
dw = 0;
for (i = 0; i < (int) pwrite->avihdr.dwStreams; i++) {
dw += pwrite->cbFormat[i] + 512;
}
// Reserve some space for the header
pwrite->dwStart = max(4096L, dw);
mmioSeek(pwrite->hmmio, pwrite->dwStart, SEEK_SET);
if (pwrite->avihdr.dwFlags & AVIF_ISINTERLEAVED) {
/* Start the 'rec' list */
pwrite->ckRECORD.cksize = 0;
pwrite->ckRECORD.fccType = listtypeAVIRECORD;
if (mmioCreateChunk(pwrite->hmmio, &pwrite->ckRECORD, MMIO_CREATELIST)) {
goto FileError;
}
pwrite->dwIndexRec = pwrite->dwIndex;
if (!AddChunkToIndex(pwrite, &pwrite->ckRECORD, AVIIF_LIST))
return AVIERR_MEMORY;
}
}
if (!(dwFlags & AVIIF_NOTIME)) {
if (pwrite->strhdr[stream].dwSampleSize)
pwrite->strhdr[stream].dwLength += cbData / pwrite->strhdr[stream].dwSampleSize;
else
++pwrite->strhdr[stream].dwLength;
}
// ck.ckid = MAKEAVICKID(cktype, stream);
ck.ckid = ((LONG) cktype << 16) | ('0' << 8) | ('0' + stream);
ck.cksize = cbData;
if (mmioCreateChunk(pwrite->hmmio, &ck, 0))
goto FileError;
if (mmioWrite(pwrite->hmmio, lpData, cbData) != (LONG) cbData)
goto FileError;
if (mmioAscend(pwrite->hmmio, &ck, 0))
goto FileError;
if (!AddChunkToIndex(pwrite, &ck, dwFlags))
return AVIERR_MEMORY;
return AVIERR_OK;
FileError:
return AVIERR_FILEWRITE;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileEndRecord | Mark the end of a record of data for
* interleaved files.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileWrite
****************************************************************************/
LONG FAR avifileEndRecord(HAVI hfile)
{
PPHYS pwrite = (PPHYS) hfile;
if (pwrite->avihdr.dwFlags & AVIF_ISINTERLEAVED) {
/* !!!! Pad here? */
if (mmioAscend(pwrite->hmmio, &pwrite->ckRECORD, 0))
goto FileError;
pwrite->hpIndex[pwrite->dwIndexRec].dwChunkLength = pwrite->ckRECORD.cksize;
/* Start the next 'rec' list */
pwrite->ckRECORD.cksize = 0;
pwrite->ckRECORD.fccType = listtypeAVIRECORD;
if (mmioCreateChunk(pwrite->hmmio, &pwrite->ckRECORD, MMIO_CREATELIST)) {
goto FileError;
}
pwrite->dwIndexRec = pwrite->dwIndex;
if (!AddChunkToIndex(pwrite, &pwrite->ckRECORD, AVIIF_LIST))
return AVIERR_MEMORY;
}
return AVIERR_OK;
FileError:
return AVIERR_FILEWRITE;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileClose | Finish writing and close an AVI file.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileOpen
****************************************************************************/
LONG FAR avifileClose(HAVI hfile)
{
PPHYS pwrite = (PPHYS) hfile;
int stream;
MMCKINFO ck;
MMCKINFO ckRIFF;
MMCKINFO ckLIST;
MMCKINFO ckStream;
LONG lCur;
LONG lRet = AVIERR_OK;
// Make the main header
if (pwrite->avihdr.dwMicroSecPerFrame == 0) {
pwrite->avihdr.dwMicroSecPerFrame =
muldiv32(1000000L,
pwrite->strhdr[0].dwScale,
pwrite->strhdr[0].dwRate);
}
pwrite->avihdr.dwFlags |= AVIF_HASINDEX;
pwrite->avihdr.dwFlags &= ~(AVIF_ISINTERLEAVED | AVIF_WASCAPTUREFILE |
AVIF_MUSTUSEINDEX);
pwrite->avihdr.dwTotalFrames = pwrite->strhdr[0].dwLength; // !!!
pwrite->avihdr.dwInitialFrames = 0; // !!!
// Go back and write out the header
lCur = mmioSeek(pwrite->hmmio, 0, SEEK_CUR);
mmioSeek(pwrite->hmmio, 0, SEEK_SET);
/* Create RIFF chunk */
ckRIFF.cksize = 0;
ckRIFF.fccType = formtypeAVI;
if (mmioCreateChunk(pwrite->hmmio, &ckRIFF, MMIO_CREATERIFF)) {
goto FileError;
}
/* Create header list */
ckLIST.cksize = 0;
ckLIST.fccType = listtypeAVIHEADER;
if (mmioCreateChunk(pwrite->hmmio, &ckLIST, MMIO_CREATELIST)) {
goto FileError;
}
/* Create AVI header chunk */
ck.cksize = sizeof(pwrite->avihdr);
ck.ckid = ckidAVIMAINHDR;
if (mmioCreateChunk(pwrite->hmmio, &ck, 0)) {
goto FileError;
}
/* Write AVI header info */
if (mmioWrite(pwrite->hmmio,
(LPSTR)&pwrite->avihdr,
sizeof(pwrite->avihdr)) != sizeof(pwrite->avihdr)) {
goto FileError;
}
if (mmioAscend(pwrite->hmmio, &ck, 0)) {
goto FileError;
}
for (stream = 0; stream < (int) pwrite->avihdr.dwStreams; stream++) {
/* Create stream header list */
ckStream.cksize = 0;
ckStream.fccType = listtypeSTREAMHEADER;
if (mmioCreateChunk(pwrite->hmmio,&ckStream,MMIO_CREATELIST)) {
goto FileError;
}
ck.ckid = ckidSTREAMHEADER;
if (mmioCreateChunk(pwrite->hmmio, &ck, 0)) {
goto FileError;
}
if (mmioWrite(pwrite->hmmio,
(LPVOID) &pwrite->strhdr[stream],
sizeof(pwrite->strhdr[stream])) != sizeof(pwrite->strhdr[stream])) {
goto FileError;
}
if (mmioAscend(pwrite->hmmio, &ck, 0)) {
goto FileError;
}
ck.cksize = pwrite->cbFormat[stream];
ck.ckid = ckidSTREAMFORMAT;
if (mmioCreateChunk(pwrite->hmmio, &ck, 0))
goto FileError;
if (mmioWrite(pwrite->hmmio, pwrite->lpFormat[stream], ck.cksize) !=
(LONG) ck.cksize)
goto FileError;
if (mmioAscend(pwrite->hmmio, &ck, 0))
goto FileError;
/* Ascend out of stream's header */
if (mmioAscend(pwrite->hmmio, &ckStream, 0)) {
goto FileError;
}
}
/* ascend from the Header list */
if (mmioAscend(pwrite->hmmio, &ckLIST, 0)) {
goto FileError;
}
/* Pad this header out so that the real data will start on a 2K
** boundary by writing a JUNK chunk.
*/
ck.ckid = ckidAVIPADDING;
if (mmioCreateChunk(pwrite->hmmio,&ck,0)) {
goto FileError;
}
if (mmioSeek(pwrite->hmmio, 0, SEEK_CUR) >
(LONG) (pwrite->dwStart - 3 * sizeof(DWORD))) {
// !!! Ack: we didn't leave enough space for the header.
// !!! How can we avoid this?
goto FileError;
}
mmioSeek(pwrite->hmmio, pwrite->dwStart - 3 * sizeof(DWORD), SEEK_SET);
if (mmioAscend(pwrite->hmmio, &ck, 0)) {
goto FileError;
}
/* Start the 'movi' list, where all of the actual data will be. */
ckLIST.cksize = 0;
ckLIST.fccType = listtypeAVIMOVIE;
if (mmioCreateChunk(pwrite->hmmio, &ckLIST, MMIO_CREATELIST)) {
goto FileError;
}
mmioSeek(pwrite->hmmio, lCur, SEEK_SET);
if (mmioAscend(pwrite->hmmio, &ckLIST, 0))
goto FileError;
/*
** Now write index out!
*/
if (!WriteIndex(pwrite))
goto FileError;
FinishUp:
if (mmioAscend(pwrite->hmmio, &ckRIFF, 0))
goto FileError;
if (mmioFlush(pwrite->hmmio, 0))
goto FileError;
/* Close the file */
if (mmioClose(pwrite->hmmio, 0))
goto FileError;
GlobalFreePtr(pwrite); // C6 doesn't like this line
return lRet;
FileError:
lRet = AVIERR_FILEWRITE;
goto FinishUp;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/* Internal indexing functions.... */
#define INDEXALLOC 256
static BOOL NEAR PASCAL InitIndex(PPHYS pphys)
{
pphys->hpIndex = (AVIINDEXENTRY huge *)
GlobalAllocPtr(GMEM_MOVEABLE,
INDEXALLOC * sizeof(AVIINDEXENTRY));
if (!pphys->hpIndex)
return FALSE;
pphys->dwIndex = 0;
pphys->dwIndexAlloc = INDEXALLOC;
return TRUE;
}
static BOOL NEAR PASCAL AddChunkToIndex(PPHYS pphys, MMCKINFO FAR *lpck, DWORD dwFlags)
{
if (pphys->dwIndex == pphys->dwIndexAlloc) {
AVIINDEXENTRY huge * hp;
hp = (AVIINDEXENTRY huge *)
GlobalReAllocPtr(pphys->hpIndex,
(pphys->dwIndexAlloc + INDEXALLOC) * sizeof(AVIINDEXENTRY),
GMEM_MOVEABLE);
if (!hp)
return FALSE;
pphys->hpIndex = hp;
pphys->dwIndexAlloc += INDEXALLOC;
}
/* Record the position of the chunk we just wrote out. */
pphys->hpIndex[pphys->dwIndex].ckid = lpck->ckid;
pphys->hpIndex[pphys->dwIndex].dwChunkLength = lpck->cksize;
/* dwChunkOffset is the offset of the chunk itself, not the
** data contained in the chunk....
*/
// !!! fix to write out relative indexes!
pphys->hpIndex[pphys->dwIndex].dwChunkOffset =
lpck->dwDataOffset - 2 * sizeof(DWORD);
pphys->hpIndex[pphys->dwIndex].dwFlags = dwFlags;
pphys->dwIndex++;
return TRUE;
}
static BOOL NEAR PASCAL WriteIndex(PPHYS pphys)
{
MMCKINFO ck;
ck.ckid = ckidAVINEWINDEX;
ck.cksize = sizeof(AVIINDEXENTRY) * pphys->dwIndex;
if (mmioCreateChunk(pphys->hmmio, &ck, 0))
return FALSE;
if (mmioWrite(pphys->hmmio, (HPSTR) pphys->hpIndex, ck.cksize) !=
(LONG) ck.cksize)
return FALSE;
if (mmioAscend(pphys->hmmio, &ck, 0))
return FALSE;
return TRUE;
}
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | aviVideoOpen | Helper function for writing AVI files consisting
* only of a single video stream.
*
* @parm HAVI FAR * | lphfile | Holds the returned file handle if the
* function succeeds.
*
* @parm LPSTR | lpFileName | The file name to use.
*
* @parm LPBITMAPINFOHEADER | lpbi | The format of the video to be written.
*
* @parm DWORD | dwMicroSecPerFrame | The spacing of frames in time. If
* zero, a default of 15 frames/sec will be used.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileOpen
****************************************************************************/
LONG FAR aviVideoOpen(HAVI FAR *lphfile,
LPSTR lpFileName,
LPBITMAPINFOHEADER lpbi,
DWORD dwMicroSecPerFrame)
{
LONG l;
MainAVIHeader hdrNew;
AVIStreamHeader strhdr;
if (dwMicroSecPerFrame == 0)
dwMicroSecPerFrame = 1000000L/15;
_fmemset(&hdrNew, 0, sizeof(hdrNew));
hdrNew.dwMicroSecPerFrame = dwMicroSecPerFrame;
hdrNew.dwMaxBytesPerSec = 0;
hdrNew.dwPaddingGranularity = 0;
hdrNew.dwFlags = AVIF_HASINDEX;
hdrNew.dwTotalFrames = 0;
hdrNew.dwStreams = 1;
hdrNew.dwSuggestedBufferSize = 0;
hdrNew.dwWidth = lpbi->biWidth;
hdrNew.dwHeight = lpbi->biHeight;
l = avifileOpen(lphfile, lpFileName, &hdrNew);
if (l != AVIERR_OK)
return l;
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;
strhdr.fccHandler = 0;
strhdr.dwScale = dwMicroSecPerFrame;
strhdr.dwRate = 1000000;
strhdr.dwSuggestedBufferSize = lpbi->biSizeImage;
l = avifileAddStream(*lphfile, NULL, &strhdr, lpbi,
lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
if (l != AVIERR_OK)
avifileClose(*lphfile);
return l;
}
/*****************************************************************************
* @doc EXTERNAL
*
* @api LONG | avifileWrite | Write data to an AVI file.
*
* @parm HAVI | hfile | The handle returned from <f avifileOpen>.
*
* @parm LPBITMAPINFOHEADER | lpbi | The format of the frame to write.
*
* @parm LPVOID | lpData | A pointer to the bits to write.
*
* @parm DWORD | dwFlags | Flags associated with this data. In particular:
*
* @flag AVIIF_KEYFRAME | This data represents a key frame.
*
* @rdesc Returns AVIERR_OK if successful, an error code otherwise.
*
* @xref avifileWrite
****************************************************************************/
LONG FAR aviVideoWriteFrame(HAVI hfile, LPBITMAPINFOHEADER lpbi, LPVOID lp, DWORD dwFlags)
{
if (lpbi == NULL)
return -1;
if (lp == NULL)
lp = (LPBYTE) lpbi + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
return avifileWrite(hfile, 0, lp, lpbi->biSizeImage,
(lpbi->biCompression ? cktypeDIBcompressed : cktypeDIBbits),
dwFlags);
}